package com.appspot.javametronome.client.controller;

import com.appspot.javametronome.client.model.Bpm;
import com.appspot.javametronome.client.view.View;

public class BpmController {

    public static final BpmController instance = new BpmController(new Bpm());
    private Bpm bpm;
    private long curr, prev, delta, tempDelta, sum;
    private View view;

    private BpmController(Bpm bpm) {
        this.bpm = bpm;
        populateDeltas();
    }
    
    public void setView(View view) {
        this.view = view;
    }

    private void populateDeltas() {
        long initDelta = bpmToDelta(bpm.getDoubleValue());
        long[] deltas = bpm.getDeltas();
        for (int i = 0; i < deltas.length; i++) {
            deltas[i] = initDelta;
        }
    }

    public Bpm getBpm() {
        return bpm;
    }

    /**
     * This should be called when entering or exiting tap mode.
     */
    public void refresh() {
        prev = 0;
        populateDeltas();
    }

    /**
     * This should be called when the Tap button is pressed.
     * This algorithm represents a sliding window of a number deltas specified by Bpm.sampleSize.
     * Each delta represents the milliseconds between the two consecutive taps.
     * The deltas are FIFO ordered.
     */
    public void onTap() {
        long[] deltas = bpm.getDeltas();
        curr = System.currentTimeMillis();
        if (prev == 0) {
            prev = curr - deltas[0];
        }
        sum = delta = curr - prev;
        prev = curr;
        for (int i = 0; i < deltas.length; i++) {
            tempDelta = deltas[i];
            sum += tempDelta;
            deltas[i] = delta;
            delta = tempDelta;
        }
        bpm.setBpm(deltaToBpm(sum / bpm.getSampleSize()));
        
        if (view != null) {
            view.bpmChanged(bpm);
        }
    }
    
    public static long bpmToDelta(double bpm) {
        return (long) (1000.0 / (bpm / 60.0));
    }

    public static double deltaToBpm(long delta) {
        return (1000.0 / delta) * 60.0;
    }

    public View getView() {
        return view;
    }

}
